// See LICENSE for license details.
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include "nuclei_sdk_soc.h"

#define ILM_RAM_START               0x08000000
#define ILM_RAM_SIZE                0x00002000
#define ILM_RAM_SPLIT               0x00001

#define READ_U8(addr)               (*(volatile uint8_t  *) ((addr)) )
#define WRITE_U8(addr, val)         (*(volatile uint8_t  *) ((addr)) )= (uint8_t)(val)

#define READ_U16(addr)              (*(volatile uint16_t *) ((addr)) )
#define WRITE_U16(addr, val)        (*(volatile uint16_t *) ((addr)) )= (uint16_t)(val)

#define READ_U32(addr)              (*(volatile uint32_t *) ((addr)) )
#define WRITE_U32(addr, val)        (*(volatile uint32_t *) ((addr)) )= (uint32_t)(val)

#define READ_U64(addr)              (*((volatile uint64_t *) ((unsigned long)(addr))) )
#define WRITE_U64(addr, val)        (*((volatile uint64_t *) ((unsigned long)(addr))) )= (uint64_t)(val)



void show_progress(uint32_t index, uint32_t size)
{
    uint32_t percent = 0;
    if ((index % 0x1000) == 0) {
        percent = (index * 100) / size;
        printf("\rCurrent progress %02d %%\t\t", percent);
    }
}

int32_t memtest_32(uint32_t start, uint32_t size)
{
    uint32_t i = 0;
    uint32_t val = 0;
    int32_t fail_count = 0;

    printf("Start to memory test for 32 bit access!\r\n");
    printf("Start to write ILM memory!\r\n");
    for (i = 0; i < size; i +=4) {
        WRITE_U32(start+i, i);
        show_progress(i, size);
    }
    printf("\nStart to compare ILM memory!\r\n");
    for (i = 0; i < size; i +=4) {
        val = READ_U32(start+i);
        show_progress(i, size);
        if (val != i) {
            fail_count ++;
            printf("\n===Data mismatch @0x%08x, %u - %u\n", (start+i), val, i);
        }
    }
    if (fail_count > 0) {
        printf("\n///Compare failed(count = %u) for 32 bit access from 0x%08x - 0x%08x!\n", \
            fail_count, start, start+size);
    } else {
        printf("\nCompare finished for 32 bit access!\n");
    }
    return fail_count;
}

int32_t memtest_16(uint32_t start, uint32_t size)
{
    uint32_t i = 0;
    uint32_t val = 0;
    int32_t fail_count = 0;

    printf("Start to memory test for 16 bit access!\r\n");
    printf("Start to write ILM memory!\r\n");
    for (i = 0; i < size; i +=2) {
        WRITE_U16(start+i, i);
        show_progress(i, size);
    }
    printf("\nStart to compare ILM memory!\r\n");
    for (i = 0; i < size; i +=2) {
        val = READ_U16(start+i);
        show_progress(i, size);
        if ((uint16_t)val != (uint16_t)i) {
            printf("\n===Data mismatch @0x%08x, %u - %u\n", (start+i), (uint16_t)val, (uint16_t)i);
            fail_count ++;
        }
    }
    if (fail_count > 0) {
        printf("\n///Compare failed(count = %u) for 16 bit access from 0x%08x - 0x%08x!\n", \
            fail_count, start, start+size);
    } else {
        printf("\nCompare finished for 16 bit access!\n");
    }
    return fail_count;
}

int32_t memtest_8(uint32_t start, uint32_t size)
{
    uint32_t i = 0;
    uint32_t val = 0;
    int32_t fail_count = 0;

    printf("Start to memory test for 8 bit access!\r\n");
    printf("Start to write ILM memory!\r\n");
    for (i = 0; i < size; i +=1) {
        show_progress(i, size);
        WRITE_U8(start+i, i);
    }
    printf("\nStart to compare ILM memory!\r\n");
    for (i = 0; i < size; i +=1) {
        val = READ_U8(start+i);
        show_progress(i, size);
        if ((uint8_t)val != (uint8_t)i) {
            printf("\n===Data mismatch @0x%08x, %u - %u\n", (start+i), (uint8_t)val, (uint8_t)i);
            fail_count ++;
        }
    }
    if (fail_count > 0) {
        printf("\n///Compare failed(count = %u) for 8 bit access from 0x%08x - 0x%08x!\n", \
            fail_count, start, start+size);
    } else {
        printf("\nCompare finished for 8 bit access!\n");
    }
    return fail_count;
}

int32_t memtest_64(uint32_t start, uint32_t size)
{
    uint64_t i = 0;
    uint64_t val = 0;
    int32_t fail_count = 0;

    printf("Start to memory test for 64 bit access!\r\n");
    printf("Start to write ILM memory!\r\n");
    for (i = 0; i < size; i +=8) {
        show_progress(i, size);
        WRITE_U64(start+i, i);
    }
    printf("\nStart to compare ILM memory!\r\n");
    for (i = 0; i < size; i +=8) {
        val = READ_U64(start+i);
        show_progress(i, size);
        if ((uint64_t)val != (uint64_t)i) {
            printf("\n===Data mismatch @0x%016x, %u - %u\n", (start+i), (uint64_t)val, (uint64_t)i);
            fail_count ++;
        }
    }
    if (fail_count > 0) {
        printf("\n///Compare failed(count = %u) for 64 bit access from 0x%08x - 0x%08x!\n", \
            fail_count, start, start+size);
    } else {
        printf("\nCompare finished for 64 bit access!\n");
    }
    return fail_count;
}

void  memtest_all_back(uint32_t start, uint32_t size)
{
    int32_t fail_count = 0;
    size = size + start;

    for (uint32_t index = start; index < size; index += ILM_RAM_SPLIT) {
        printf("\nMemory test for address 0x%08x - 0x%08x!\n", index, index + ILM_RAM_SPLIT);
        fail_count += memtest_8(index, ILM_RAM_SPLIT);
        fail_count += memtest_16(index, ILM_RAM_SPLIT);
        fail_count += memtest_32(index, ILM_RAM_SPLIT);
        fail_count += memtest_64(index, ILM_RAM_SPLIT);
    }
    printf("Total fail count %u\n", fail_count);
    
}


int32_t memtest_all(uint32_t start, uint32_t size)
{
    int32_t fail_count = 0;
    size = size + start;

    //for (uint32_t index = start; index < size; index += ILM_RAM_SPLIT) {
        printf("\nMemory test for address 0x%08x - 0x%08x!\n", start, start + ILM_RAM_SPLIT);
        fail_count += memtest_8(start, ILM_RAM_SPLIT);
        fail_count += memtest_16(start, ILM_RAM_SPLIT);
        fail_count += memtest_32(start, ILM_RAM_SPLIT);
        fail_count += memtest_64(start, ILM_RAM_SPLIT);
  //  }
    printf("Total fail count %u\n", fail_count);
    return fail_count;
}

void print_misa(void)
{
    CSR_MISA_Type misa_bits = (CSR_MISA_Type) __RV_CSR_READ(CSR_MISA);
    static char misa_chars[30];
    uint8_t index = 0;
    if (misa_bits.b.mxl == 1) {
        misa_chars[index++] = '3';
        misa_chars[index++] = '2';
    } else if (misa_bits.b.mxl == 2) {
        misa_chars[index++] = '6';
        misa_chars[index++] = '4';
    } else if (misa_bits.b.mxl == 3) {
        misa_chars[index++] = '1';
        misa_chars[index++] = '2';
        misa_chars[index++] = '8';
    }
    if (misa_bits.b.i) {
        misa_chars[index++] = 'I';
    }
    if (misa_bits.b.m) {
        misa_chars[index++] = 'M';
    }
    if (misa_bits.b.a) {
        misa_chars[index++] = 'A';
    }
    if (misa_bits.b.b) {
        misa_chars[index++] = 'B';
    }
    if (misa_bits.b.c) {
        misa_chars[index++] = 'C';
    }
    if (misa_bits.b.e) {
        misa_chars[index++] = 'E';
    }
    if (misa_bits.b.f) {
        misa_chars[index++] = 'F';
    }
    if (misa_bits.b.d) {
        misa_chars[index++] = 'D';
    }
    if (misa_bits.b.q) {
        misa_chars[index++] = 'Q';
    }
    if (misa_bits.b.h) {
        misa_chars[index++] = 'H';
    }
    if (misa_bits.b.j) {
        misa_chars[index++] = 'J';
    }
    if (misa_bits.b.l) {
        misa_chars[index++] = 'L';
    }
    if (misa_bits.b.n) {
        misa_chars[index++] = 'N';
    }
    if (misa_bits.b.s) {
        misa_chars[index++] = 'S';
    }
    if (misa_bits.b.p) {
        misa_chars[index++] = 'P';
    }
    if (misa_bits.b.t) {
        misa_chars[index++] = 'T';
    }
    if (misa_bits.b.u) {
        misa_chars[index++] = 'U';
    }
    if (misa_bits.b.x) {
        misa_chars[index++] = 'X';
    }

    misa_chars[index++] = '\0';

    printf("MISA: RV%s\r\n", misa_chars);
}

int main(void)
{
    
    int32_t fail_count_g = 0;
    // adjust cache configuration here
    __RV_CSR_SET(CSR_MCACHE_CTL, CSR_MCACHE_CTL_DE|CSR_MCACHE_CTL_IE);

    srand(0x02C68D98);

    while(1){

        uint32_t num = rand()%(ILM_RAM_SIZE-1);    
        fail_count_g =  memtest_all((ILM_RAM_START+num), ILM_RAM_SIZE);
        if(fail_count_g > 0){
                printf("sram has Error !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\r\n"); 
                return -1 ;
        }
    }
       
    return 0;
}

